#pragma once

#include <iostream>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>

#include "libmri.h"
#include "l1spirit_config.h"

void l1spirit (cplxf *data, int *dsize, l1spirit_config const&);

void rescale_data (cplxf *data, int *, float& scale, l1spirit_config const&);
void unscale_data (cplxf *data, int *, float scaler);
void calib_size (int& si, int& sj, int *mask, int M, int N);
void crop_calib_data (cplxf *&calib, int*, cplxf *data, int *, int nr);
void ifft_readout (cplxf *data, int *dsize);
void fft_readout (cplxf *data, int *dsize);
void ifft_kern_readout (cplxf *&k2d, cplxf *k3d, int *ksize, int *dsize);

/*
void calib_kern_3d (cplxf*& k3d, int *ksize,
                     cplxf * calib, int *csize, float epsilon);
*/

int sampling_pattern (int*& nzmask, cplxf *data, int *dsize);
void solver_mask (float *&mask, cplxf *X, int xs0, int m, int n);

static inline void pad_indices (int& i0, int& j0, int M2, int N2, int M, int N){
	i0 = pad_index (M2, M);
	j0 = pad_index (N2, N);
}

#define l1spirit_debug(config,field,idx,array,ndims,...) do{\
	if (config.debug[l1spirit_config::debug_##field]){\
		char shm_name[1024];\
		sprintf (shm_name, config.debug[l1spirit_config::debug_##field], idx);\
		debug_shm (shm_name,array,ndims, ##__VA_ARGS__);\
	}\
} while(0)




template <typename T>
inline T* __dbg_allocate (int& N, T *array, int ndims, ...)
{
	va_list ap;
	va_start (ap, ndims);

	N = 1;
	for (int i = 0; i < ndims; ++i){
		N = N*va_arg (ap, int);
	}

	return new T[N];
}

#define l1spirit_cudebug(config,field,idx,array,ndims,...) do{\
    if (config.debug[l1spirit_config::debug_##field]){\
        int N; typeof(array) p = __dbg_allocate(N, array, ndims, ##__VA_ARGS__);\
        cuda(Memcpy(p,array,N*sizeof(*array),cudaMemcpyDeviceToHost));\
        l1spirit_debug(config,field,idx,p,ndims,##__VA_ARGS__);\
        delete [] p;\
    }\
} while(0)
